﻿using System;
using System.Collections.Specialized;
using System.Linq;
using LinqToLfsWorld;
using LinqToLfsWorld.Extensions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace LibraryTest
{

	/// <summary>
	/// Summary description for QueryTest
	/// </summary>
	[TestClass]
	public class QueryTest
	{
		public QueryTest()
		{

		}

		private TestContext testContextInstance;
		private static LfsWorldContext lfsContext;

		/// <summary>
		///Gets or sets the test context which provides
		///information about and functionality for the current test run.
		///</summary>
		public TestContext TestContext
		{
			get
			{
				return testContextInstance;
			}
			set
			{
				testContextInstance = value;
			}
		}

		#region Additional test attributes
		//
		// You can use the following additional attributes as you write your tests:
		//
		// Use ClassInitialize to run code before running the first test in the class
		[ClassInitialize()]
		public static void MyClassInitialize(TestContext testContext) 
		{
			lfsContext = new LfsWorldContext();
		}
		
		// Use ClassCleanup to run code after all tests in a class have run
		[ClassCleanup()]
		public static void MyClassCleanup() 
		{
			lfsContext.Dispose();
		}

		#endregion


		#region Exception Tests

		[TestMethod]
		[ExpectedException(typeof(LinqToLfsWorld.PubstatQueryException))]
		public void InvalidOperatorTest()
		{
			// Fire off an invalid query on RacerFuels - should throw an exception because the > query type
			// isn't supported
			var query = lfsContext.RacerFuels.Where(f => f.RacerName.Length > 1);
			query.ToList();

		}

		[TestMethod]
		[ExpectedException(typeof(LinqToLfsWorld.PubstatQueryException))]
		public void InvalidQueryPropertyTest()
		{
			// Fire off an invalid query. This time, anything other than 'RacerName' is not queryable and should
			// throw an exception
			var query = lfsContext.RacerFuels.Where(f => f.TrackIdent == "000");

			query.ToList();
		}

		[TestMethod]
		[ExpectedException(typeof(LinqToLfsWorld.PubstatQueryException))]
		public void InvalidMethodTest()
		{
			var query = from h in lfsContext.Hosts
						orderby h.HostName
						select h;

			// PubstatQueryException - OrderBy not supported
			query.ToList();
		}

		#endregion


		#region Query Selection Methods Tests

		[TestMethod]
		public void SingleSelectionTest()
		{
			var stat = lfsContext.RacerStats.Where(s => s.RacerName == "elkdanger").Single();
		}

		[TestMethod]
		public void FirstSelectionTest()
		{
			var stat = lfsContext.RacerStats.Where(s => s.RacerName == "elkdanger").First();
		}

		[TestMethod]
		public void LastSelectionTest()
		{
			var stat = lfsContext.RacerStats.Where(s => s.RacerName == "elkdanger").Last();
		}

		[TestMethod]
		public void CountTest()
		{
			int count = lfsContext.RacerStats.Where(s => s.RacerName == "elkdanger").Count();

			Assert.AreEqual(1, count);
		}

		#endregion


		#region Racer Stats Query Test

		[TestMethod]
		public void RacerStatsTest()
		{
			lfsContext.RequestMade += new LfsRequestHandler(RacerStats_RequestMade);

			var query = lfsContext.RacerStats.Where(rs => rs.RacerName == "elkdanger");
			query.ToList();

			lfsContext.RequestMade -= new LfsRequestHandler(this.RacerStats_RequestMade);
		}

		void RacerStats_RequestMade(object sender, LfsWorldResponse response)
		{
			StringDictionary values = GetRequestValues(response.RequestUrl);

			ValidateRequestValue("racer", "elkdanger", values);
		}

		#endregion


		#region Racer Fuel Query Test

		[TestMethod]
		public void RacerFuelQueryTest()
		{

			lfsContext.RequestMade += new LfsRequestHandler(RacerFuel_RequestMade);

			lfsContext.RacerFuels.Where(f => f.RacerName == "elkdanger").ToList();

			lfsContext.RequestMade -= new LfsRequestHandler(RacerFuel_RequestMade);
		}

		void RacerFuel_RequestMade(object sender, LfsWorldResponse response)
		{
			StringDictionary values = GetRequestValues(response.RequestUrl);

			ValidateRequestValue("racer", "elkdanger", values);
		}

		#endregion


		#region Personal Bests Query Test

		[TestMethod]
		public void PersonalBestsQueryTest()
		{
			lfsContext.RequestMade += new LfsRequestHandler(PersonalBests_RequestMade);

			var query = lfsContext.PersonalBests.Where(rs => rs.RacerName == "elkdanger");
			query.ToList();

			lfsContext.RequestMade -= new LfsRequestHandler(this.PersonalBests_RequestMade);
		}

		void PersonalBests_RequestMade(object sender, LfsWorldResponse response)
		{
			StringDictionary values = GetRequestValues(response.RequestUrl);

			ValidateRequestValue("racer", "elkdanger", values);
		}

		#endregion


		#region World Record Query Test

		[TestMethod]
		public void WorldRecordQueryTest()
		{

			lfsContext.RequestMade += new LfsRequestHandler(WorldRecord_RequestMade);

			var query = from wr in lfsContext.WorldRecords
						where wr.Car == CarTypes.XRG
						&& wr.TrackIdent == "000"
						select wr;

			query.ToList();

			lfsContext.RequestMade -= new LfsRequestHandler(WorldRecord_RequestMade);
		}

		void WorldRecord_RequestMade(object sender, LfsWorldResponse response)
		{
			StringDictionary values = GetRequestValues(response.RequestUrl);

			ValidateRequestValue("track", "000", values);
			ValidateRequestValue("car", "XRG", values);
		}

		#endregion


		#region Hotlap Query Test

		[TestMethod]
		public void HotlapQueryTest()
		{

			lfsContext.RequestMade += new LfsRequestHandler(HotlapQuery_RequestMade);

			lfsContext.Hotlaps.Where(hl => hl.RacerName == "elkdanger");

			lfsContext.RequestMade -= new LfsRequestHandler(HotlapQuery_RequestMade);
		}

		void HotlapQuery_RequestMade(object sender, LfsWorldResponse response)
		{
			StringDictionary values = GetRequestValues(response.RequestUrl);

			ValidateRequestValue("racer", "elkdanger", values);
		}

		#endregion


		#region HotlapChart Query Test

		[TestMethod]
		public void HotlapChartQueryTest()
		{

			lfsContext.RequestMade += new LfsRequestHandler(HotlapChart_RequestMade);

			var query = from hlc in lfsContext.HotlapChart
						where hlc.TrackIdent == CircuitTypes.BlackwoodGP
						&& hlc.Car == CarTypes.XRG
						&& hlc.SteeringMethod == SteeringTypes.Mouse
						select hlc;

			query.ToList();

			lfsContext.RequestMade -= new LfsRequestHandler(HotlapChart_RequestMade);
		}

		void HotlapChart_RequestMade(object sender, LfsWorldResponse response)
		{
			StringDictionary values = GetRequestValues(response.RequestUrl);

			ValidateRequestValue("track", "000", values);
			ValidateRequestValue("car", "XRG", values);
			ValidateRequestValue("control", "m", values);
		}

		#endregion


		#region HotlapLog Query Test

		private int _hotlapLogTime;

		[TestMethod]
		public void HotlapLogQueryTest()
		{
			_hotlapLogTime = DateTime.Now.AddMonths(-1).ToUnixTimestamp();

			lfsContext.RequestMade += new LfsRequestHandler(HotlapLog_RequestMade);

			var query = from hll in lfsContext.HotlapLog
						where hll.Steering == SteeringTypes.Mouse
						&& hll.LogFilter == HotlapLogFilter.Top10
						&& hll.StartTime == _hotlapLogTime
						&& hll.Lines == 90
						select hll;

			query.ToList();

			lfsContext.RequestMade -= new LfsRequestHandler(HotlapLog_RequestMade);
		}

		void HotlapLog_RequestMade(object sender, LfsWorldResponse response)
		{
			StringDictionary values = GetRequestValues(response.RequestUrl);

			ValidateRequestValue("log_filter", ((int)HotlapLogFilter.Top10).ToString(), values);
			ValidateRequestValue("control", "m", values);
			ValidateRequestValue("lines", "90", values);
			ValidateRequestValue("starttime", _hotlapLogTime.ToString(), values);
		}

		#endregion


		#region Hosts Query Test

		[TestMethod]
		public void HostsQueryTest()
		{
			var hosts = (from host in lfsContext.Hosts select host).ToList();

			Assert.AreNotEqual(0, hosts.Count);
		}

		[TestMethod]
		public void TeamsQueryTest()
		{
			var teams = (from team in lfsContext.Teams select team).ToList();

			Assert.AreNotEqual(0, teams.Count);
		}

		[TestMethod]
		public void DualQueryTest()
		{

			lfsContext.PremiumPubstatAccess = false;

			var racerStats = (from stat in lfsContext.RacerStats where stat.RacerName == "elkdanger" select stat).ToArray();
			var teams = (from team in lfsContext.Teams select team).ToArray();

			lfsContext.PremiumPubstatAccess = true;
		}

		#endregion


		#region Helper Methods


		/// <summary>
		/// Validates the request value.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <param name="expectedValue">The expected value.</param>
		/// <param name="values">The values.</param>
		/// <exception cref="PubstatQueryException">The key's value did not contain the expected value.</exception>
		private void ValidateRequestValue(string key, string expectedValue, StringDictionary values)
		{
			if (values.ContainsKey(key) == false || values[key] != expectedValue)
				throw new PubstatQueryException(key + " was not the expected value");
		}

		/// <summary>
		/// Gets the request values and parses the querystring into a StringDictionary.
		/// </summary>
		/// <param name="requestUrl">The request URL.</param>
		private StringDictionary GetRequestValues(string requestUrl)
		{
			StringDictionary values = new StringDictionary();

			string[] keyVals = requestUrl.Substring(requestUrl.IndexOf("?")).Split(new char[] { '&' });

			foreach (string s in keyVals)
			{
				string[] parts = s.Split(new char[] {'='});
				values.Add(parts[0], parts[1]);
			}

			return values;
		}

		#endregion

	}
}
